/*
 * $Id: ASCFileHandler.java,v 1.20 2009/08/20 19:17:56 navdeep_mahajan Exp $
 *
 * Copyright (C) 2006 General Electric Company. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 *
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */

package com.ge.healthcare.autosc.common.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;

/**
 * Purpose:
 * <ul>
 * <li>Provides generic methods for file manipulation like reading, writing,
 * copying, deleting etc.</li>
 * </ul>
 * 
 * @author William Oliver, Navdeep Mahajan
 * @copyright (C) 2006 The General Electric Company
 */
public class ASCFileHandler extends ASCIOHandler {

	/**
	 * Dummy constructor to prevent unwanted instances from being created
	 */
	private ASCFileHandler() {
		super();
	}

	/**
	 * Open and read file and return the content as string
	 * 
	 * @param strFileName
	 * @return File content as string
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static String readFileAsString(String strFileName)
			throws FileNotFoundException, IOException {
		StringBuffer fileContent = new StringBuffer();
		FileReader input = null;
		// DataInputStream input = null;
		try {

			input = new FileReader(strFileName);
			int c;
			// String strLine = null;
			while ((c = input.read()) != -1) {
				fileContent.append((char) c);
			}
		} catch (FileNotFoundException fnfe) {
			ASCLogger.error(ASCFileHandler.class, "readFileAsString",
					"File not found with file name " + strFileName);
			throw fnfe;
		} catch (IOException ioe) {
			ASCLogger.error(ASCFileHandler.class, "readFileAsString",
					new StringBuffer().append("Exception while reading file)")
							.append(strFileName).append(". Exception:\n")
							.append(ioe.getMessage()).toString());
			throw ioe;
		} finally {
			if (input != null) {
				input.close();
			}
		}
		return fileContent.toString();
	}

	/**
	 * @param strFileName
	 * @return Object array which will basically have String contained in them
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static Object[] readFile(String strFileName)
			throws FileNotFoundException, IOException {
		FileReader objFileReader = null;
		BufferedReader objBufferedReader = null;
		Vector<String> objFileContent = new Vector<String>();

		try {
			String strLineContent = null;
			objFileReader = new FileReader(strFileName);
			objBufferedReader = new BufferedReader(objFileReader);

			while ((strLineContent = objBufferedReader.readLine()) != null) {
				// Read the content and add to a Vector
				objFileContent.add(strLineContent);
			}
		} catch (FileNotFoundException objFileNotFound) {
			throw objFileNotFound;
		} catch (IOException objIOException) {
			throw objIOException;
		} finally {
			closeReader(objBufferedReader);
			closeReader(objFileReader);
		}

		return objFileContent.toArray();
	}

	/**
	 * Locates and reads the file inside the jar
	 * 
	 * @param strFileName
	 * @return Object array which will basically have String contained in them
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static Object[] locateAndReadFile(String strFileName)
			throws FileNotFoundException, IOException {
		BufferedReader objBufferedReader = null;
		InputStreamReader inputStream = null;
		Vector<String> objFileContent = new Vector<String>();
		InputStream stream = ASCFileHandler.class
				.getResourceAsStream(strFileName);
		if (stream == null) {
			stream = Thread.currentThread().getContextClassLoader()
					.getResourceAsStream(strFileName);
		}
		if (stream == null) {
			throw new FileNotFoundException("Failed to locate file : "
					+ strFileName);
		}
		try {
			String strLineContent = null;
			inputStream = new InputStreamReader(stream);
			objBufferedReader = new BufferedReader(inputStream);
			while ((strLineContent = objBufferedReader.readLine()) != null) {
				// Read the content and add to a Vector
				objFileContent.add(strLineContent);
			}
		} catch (FileNotFoundException objFileNotFound) {
			throw objFileNotFound;
		} catch (IOException objIOException) {
			throw objIOException;
		} finally {
			closeReader(objBufferedReader);
			closeReader(inputStream);
		}
		return objFileContent.toArray();
	}

	/**
	 * The method reads the file and returns non empty lines
	 * 
	 * @param strFileName
	 * @return
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static Object[] readNonEmptyLines(String strFileName)
			throws FileNotFoundException, IOException {
		Object[] contents = readFile(strFileName);
		List<String> nonEmptyLines = new ArrayList<String>();
		for (int iCount = 0; iCount < contents.length; iCount++) {
			String line = (String) contents[iCount];
			if (!ASCUtil.isNullOrEmpty(line)) {
				nonEmptyLines.add(line);
			}
		}
		return nonEmptyLines.toArray();
	}

	/**
	 * @param strFileName
	 *            - File Name to create or append To
	 * @param blnAppend
	 *            - If true appends to an existing file otherwise overwrites
	 * @param strText
	 *            - Text to be appended
	 * @throws IOException
	 */
	public static void writeToFile(String strFileName, boolean blnAppend,
			String strText) throws IOException {
		writeToFile(new File(strFileName), blnAppend, strText);
	}

	/**
	 * @param strFileName
	 *            - File Name to create or append To
	 * @param blnAppend
	 *            - If true appends to an existing file otherwise overwrites
	 * @param strText
	 *            - Text to be appended
	 * @throws IOException
	 */
	public static void writeToFile(File objFileName, boolean blnAppend,
			String strText) throws IOException {
		FileWriter objFileWriter = null;
		try{
			objFileWriter = new FileWriter(objFileName, blnAppend);
			objFileWriter.write(strText);// appends the string to the file
		}finally{
			closeWriter(objFileWriter);
		}
	}

	/**
	 * Method to rename the file
	 * 
	 * @param oldName
	 *            - The old file handle
	 * @param newName
	 *            - The new file handle
	 * @return - bolean indicating whether renaming was successful or not
	 */
	public static boolean rename(File oldName, File newName) {
		return oldName.renameTo(newName);
	}

	/**
	 * Rename source file to dest file.
	 * <p>
	 * </b>NOTE: This will only work if the files are on the same
	 * filesystem!</b>
	 * 
	 * @param src
	 *            Source File name
	 * @param dest
	 *            Destinatio File name
	 * @return true SUCCESS
	 * @return false ERROR. Output logged to system logger.
	 */
	public static boolean rename(String src, String dest)
			throws NullPointerException {
		String methodName = "rename";
		boolean ret = true;
		if ((ASCUtil.isNullOrEmpty(src)) || (ASCUtil.isNullOrEmpty(dest))) {
			return false;
		}
		if (src.equals(dest)) {
			return true;
		}

		// Move src file to dest file, then delete src
		try {
			File f = new File(dest);
			f.delete();
			f = new File(src);
			ret = f.renameTo(new File(dest));
			f = new File(src);
			f.delete(); // Just to be safe
		} catch (Exception e) {
			ASCLogger.error(ASCFileHandler.class, methodName,
					"Cannot rename file", e);
			ret = false;
		}
		return ret;
	}

	/**
	 * Perform a byte-wise copy of the source file to the destination file.
	 * 
	 * @param src
	 *            Source file represented as a String
	 * @param dest
	 *            Destination file represented as a String
	 * @return true SUCCESS
	 * @return false FAILURE.
	 */
	public static boolean copy(String src, String dest) {
		// Handle easy stuff first
		if ((ASCUtil.isNullOrEmpty(src)) || (ASCUtil.isNullOrEmpty(dest))) {
			return false;
		}
		if (src.equals(dest)) {
			return true;
		}
		return copy(new File(src), new File(dest));
	}

	/**
	 * Perform a byte-wise copy of the source file to the destination file.
	 * 
	 * @param src
	 *            Source <code>File</code>
	 * @param dest
	 *            Destination <code>File</code>
	 * @return true SUCCESS
	 * @return false FAILURE.
	 */
	public static boolean copy(File src, File dest) {
		String methodName = "copy";
		// Handle easy stuff first
		if ((ASCUtil.isNullOrEmptyObject(src))
				|| (ASCUtil.isNullOrEmptyObject(dest))) {
			return false;
		}
		if (src.getAbsolutePath().equals(dest.getAbsolutePath())) {
			return true;
		}
		// Now perform the copy with proper exception handling and cleanup
		FileInputStream in = null;
		FileOutputStream out = null;
		try {
			if (src.canRead() != true) {
				ASCLogger.error(ASCFileHandler.class, methodName,
						"Cannot read source file:" + src.getAbsolutePath());
				return false;
			}
			in = new FileInputStream(src);
			out = new FileOutputStream(dest);
			byte[] buff = new byte[1024];
			int nBytes = 0;
			while ((nBytes = in.read(buff)) != -1) {
				out.write(buff, 0, nBytes);
			}
			;
		} catch (IOException ioe) {
			ASCLogger.error(ASCFileHandler.class, methodName, "Error copying "
					+ src + " to " + dest, ioe);
			return false;
		} finally {
			try {
				if (in != null)
					in.close();
				if (out != null)
					out.close();
			} catch (IOException ioe) {
				ASCLogger.error(ASCFileHandler.class, methodName,
						"Unable to close the files", ioe);
			}
		}
		return true;
	}

	/**
	 * create a new unique directory.
	 * 
	 * @param parent
	 *            parent directory for the directory to be created.
	 * @param prefix
	 *            first set of characters for the directory name
	 * @return new unique directory if created successfully. null otherwise.
	 * @throws AutoSCFileIOException
	 */
	public static File createNewDir(File parentDir, String prefix)
			throws Exception {
		String methodName = "createNewDir";
		File newDir = null;
		int i = 0;
		try {
			if ((prefix == null) || (prefix.length() < 1))
				prefix = "" + System.currentTimeMillis();
			synchronized (ASCFileHandler.class) {
				newDir = new File(parentDir, prefix);
				while (newDir.exists()) {
					newDir = new File(parentDir, prefix + "." + i);
					i += 1;
				}
				if (newDir.mkdirs() == false) {
					ASCLogger
							.error(ASCFileHandler.class, methodName,
									"failed to create directories. check disk space/permissions.");
					newDir = null;
				}
			}
		} catch (Exception e) {
			String strMessage = "failed to create new directory. parent: "
					+ parentDir + " prefix: " + prefix + e.getMessage();
			ASCLogger.error(ASCFileHandler.class, methodName, strMessage, e);
			throw e;
		}
		return newDir;
	}

	/**
	 * Create temporary file
	 * 
	 * @param prefix
	 *            File prefix
	 * @param suffix
	 *            File suffix
	 * @param directory
	 *            directory where the temporary file created
	 * @return File object
	 * @throws Exception
	 *             - error creating temporary file
	 */
	public static File createTempFile(String prefix, String suffix,
			File directory) throws Exception {
		try {
			File tempFile = File.createTempFile(prefix, suffix, directory);
			return tempFile;
		} catch (Exception e) {
			String strMessage = new StringBuffer().append(
					"Failed creating temporary file.\n").append("directory: ")
					.append(directory.getName()).append(",prefix: ").append(
							prefix).append(",suffix: ").append(suffix)
					.toString();
			ASCLogger.error(ASCFileHandler.class, "createTempFile", strMessage,
					e);
			throw e;
		}
	}

	/**
	 * delete a directory tree whose root is given.
	 * 
	 * @param root
	 *            root directory or a simple file.
	 * @return true iff the whole tree is sucessfully deleted
	 */
	public static boolean deleteTree(File root) {
		String methodName = "deleteTree";
		boolean result = true;

		if ((root == null) || (root.exists() == false)) {
			return true;
		} else {
			if (root.isDirectory() == true) {
				// delete contents of the directory
				File[] f = root.listFiles();
				if (!ASCUtil.isNullOrEmptyObject(f)) {
					for (int i = 0; i < f.length; i++) {
						result = (result && deleteTree(f[i]));
					}
				}
				try {
					// delete directory itself
					if (root.delete()) {
						// result = result && true
						ASCLogger.info(ASCFileHandler.class, methodName,
								"deleted " + root);
					} else {
						result = false;
						ASCLogger.error(ASCFileHandler.class, methodName,
								"Unable to delete directory: " + root);
					}
				} catch (Exception e) {
					result = false;
				}
			} else {
				// input is a file; just delete it
				try {
					if (root.delete()) {
						ASCLogger.info(ASCFileHandler.class, methodName,
								"deleted " + root);
					} else {
						ASCLogger.error(ASCFileHandler.class, methodName,
								"Unable to delete file: " + root);
						result = false;
					}
				} catch (Exception e) {
					result = false;
				}
			}
		}
		return result;
	}
}
